﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using GigaSite.Domain.Entities;
using GigaSite.Domain.Enums;
using GigaSite.Domain.Interfaces.Repositories;
using GigaSite.Domain.IoC;
using GigaSite.Web.Areas.Catalog.Models;
using GigaSite.Web.Classes;
using GigaSite.Web.Controllers;

namespace GigaSite.Web.Areas.Catalog.Controllers
{
    /// <summary>
    /// Контроллер объявлений
    /// </summary>
    public class AdsController : BaseController
    {
        #region Создание объявления

        /// <summary>
        /// Возвращает HTML разметку для селектора категорий в окне создания объявлений
        /// </summary>
        /// <param name="categoryId">Идентификатор родительской категории</param>
        /// <param name="level">Текущий уровень категории</param>
        /// <returns>HTML разметка категории</returns>
        public ActionResult CreateAdsCategories(long categoryId, int level = 0)
        {
            // Репозиторий
            var repository = Locator.GetService<ICategoriesRepository>();

            // Запоминаем состояния
            ViewBag.level = level;

            // Загружаем категорию
            var category = repository.Load(categoryId);
            var childCategories = category.ChildCategories.ToList();

            // Отдаем партиал вью
            return PartialView("CreateAdsCategories", childCategories);
        }

        /// <summary>
        /// Отображает страницу создания объявления
        /// </summary>
        /// <returns></returns>
        public ActionResult CreateAds()
        {
            // Проверяем что пользователь авторизован, если нет то перенаправляем на страничку с просьбой регистрации
            if (CurrentUser == null)
            {
                return RedirectToAction("RegistrationRequired", "Home", new { area = "" });
            }
            
            // Навигация
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Добавить объявление", Url.Action("CreateAds"), true);

            // Подготавливаем модель
            var model = new CreateAdsModel()
                            {
                                SelectedCategoryId = -1,
                                Email = CurrentUser.Email // Берем по умолчанию Email указанный при регистрации
                            };

            // Отдаем вью
            return View(model);
        }

        /// <summary>
        /// Обрабатывает форму создания нового объявления
        /// </summary>
        /// <param name="model">Модель с данными пользователя</param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult CreateAds(CreateAdsModel model)
        {
            // Проверяем что пользователь авторизован, если нет то перенаправляем на страничку с просьбой регистрации
            if (CurrentUser == null)
            {
                return RedirectToAction("RegistrationRequired", "Home", new { area = "" });
            }

            // Навигация
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Объявление добавлено", Url.Action("CreateAds"), true);

            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var imagesRepository = Locator.GetService<IImagesRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // Проверяем что у нас объявление создается в существующей категории
            var category = categoriesRepository.Load(model.SelectedCategoryId);
            if (category == null)
            {
                throw new ObjectNotFoundException(String.Format("Категория с идентификатором {0} не найдена",model.SelectedCategoryId));
            }

            // Параметры создания
            var premoderated = Global.Settings.Value.GetValue("adsPremoderation", false);
            DateTime? expirationDate = null;
            switch (model.ExpirationDeadline)
            {
                case AdsExpirationDeadline.Never:
                    expirationDate = null;
                    break;
                case AdsExpirationDeadline.Week:
                    expirationDate = DateTime.Now.AddDays(7);
                    break;
                case AdsExpirationDeadline.OneMonth:
                    expirationDate = DateTime.Now.AddMonths(1);
                    break;
                case AdsExpirationDeadline.TwoMonth:
                    expirationDate = DateTime.Now.AddMonths(2);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            // Создаем объявление
            var ads = new Adv()
                          {
                              Category = category,
                              CityId = 1,
                              // TODO: проставлять город автоматически, пока Хабаровск
                              DateCreated = DateTime.Now,
                              Status = premoderated ? AdvStatuses.Checking : AdvStatuses.Active,
                              Title = model.Title,
                              Email = model.Email,
                              Views = 0,
                              Phones = model.Phone,
                              User = CurrentUser,
                              Content = model.AdsContent,
                              ExpirationDate = expirationDate,
                              Price = model.Price
                          };
            adsRepository.Add(ads);
            adsRepository.SubmitChanges();

            // Сохраняем картинки загруженные с объявлением
            // Сохраняем картинки в системе хранения
            var savedFiles = new List<Image>();
            for (int i = 0; i < Request.Files.Count; i++)
            {
                var file = Request.Files[i];
                if (file != null && file.ContentLength > 0 && file.ContentType.ToLower().Contains("image"))
                    savedFiles.Add(imagesRepository.SavePostedFile(file));
            }
            // Сохраняем картинки к объявлению
            foreach (var savedFile in savedFiles)
            {
                adsRepository.AddImageToAdvs(ads,savedFile);
            }
            adsRepository.SubmitChanges();

            // Отдаем данные во вью
            ViewBag.category = category;
            ViewBag.premoderated = premoderated;
            ViewBag.adsNumber = ads.Id;
            return View("AdsCreated");
        }

        #endregion

        #region Просмотр объявления

        /// <summary>
        /// Отображает объявление с указанным номером
        /// </summary>
        /// <param name="id">Номер объявления</param>
        /// <returns>Страница объявления</returns>
        public ActionResult ViewAds(long id)
        {
            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var imagesRepository = Locator.GetService<IImagesRepository>();

            // Ищем указанное объявление
            var ads = adsRepository.Load(id);
            if (ads == null)
            {
                return View("AdsNotFound",id);
            }

            // Формируем навигационную цепочку
            AddNavigationItem("Объявления", Url.Action("Index"));
            IList<Category> parentCategories = (IList<Category>) ads.Category.GetParentCategories();
            if (parentCategories.Count == 1)
            {
                parentCategories.Add(ads.Category);
            }
            string actionName = "";
            switch (parentCategories[1].Id)
            {
                case CategoryManager.AdsRealEstateCategory:
                    actionName = "RealEstate";
                    break;
                case CategoryManager.AdsAutoCategory:
                    actionName = "Auto";
                    break;
                case CategoryManager.AdsBusinessCategory:
                    actionName = "Business";
                    break;
                case CategoryManager.AdsTradeCategory:
                    actionName = "Trade";
                    break;
                case CategoryManager.AdsConstructionCategory:
                    actionName = "Construction";
                    break;
                case CategoryManager.AdsMiscCategory:
                    actionName = "Misc";
                    break;
                case CategoryManager.AdsServicesCategory:
                    actionName = "Services";
                    break;
            }
            foreach (var parentCategory in parentCategories.Skip(1))
            {
                AddNavigationItem(parentCategory.DisplayName,Url.Action(actionName,new {id = parentCategory.Id}));
            }
            AddNavigationItem(String.Format("{0} (№{1})",ads.Title,ads.Id),String.Empty,true);

            // Проверяем можем ли мы отобразить это объявление
            if (ads.Status != AdvStatuses.Active)
            {
                switch (ads.Status)
                {
                    case AdvStatuses.Checking:
                        return View("AdsOnModeration", ads);
                        break;
                    case AdvStatuses.Blocked:
                        return View("AdsBlocked", ads);
                        break;
                }
            }

            // Увеличиваем счетчик просмотров
            ads.Views = ads.Views + 1;
            adsRepository.SubmitChanges();

            // Формируем модель
            var model = new AdsModel(ads,imagesRepository);

            // Отдаем вид
            return View("FarPostAdsView", model);
        }

        /// <summary>
        /// Обрабатывает подачу жалобы на объявление
        /// </summary>
        /// <param name="id">Идентификатор объявления</param>
        /// <param name="message">Сообщение жалобы</param>
        /// <returns></returns>
        [HttpPost]
        public JsonResult Abuse(long id,string message)
        {
            try
            {
                // проверяем что у нас есть пользователь
                if (CurrentUser == null)
                {
                    throw new Exception("Вы должны авторизоваться для подачи жалобы");
                }

                // Репозитории
                var adsRepository = Locator.GetService<IAdvsRepository>();
                var abuseRepository = Locator.GetService<IAbusesRepository>();

                // Загружаем объявление
                var ads = adsRepository.Load(id);
                if (ads == null)
                {
                    throw new Exception(String.Format("Объявление с номером {0} не найдено", id));
                }

                // Проверяем есть ли жалоба на такое объявление
                if (abuseRepository.HasAbuseFromUser(CurrentUser, AbuseTypes.Ads, ads.Id))
                {
                    throw new Exception("Вы уже подавали жалобу на это объявление");
                }

                // Подаем жалобу
                var abuse = abuseRepository.AddAbuse(AbuseTypes.Ads, CurrentUser, ads.Id, message);
                abuseRepository.SubmitChanges();

                // Отдаем успешный результат
                return Json(new { id = abuse.Id });
            }
            catch (Exception e)
            {
                Response.StatusCode = 500;
                return Json(new {message = e.Message});
            }
        }

        #endregion

        #region Категории объявлений

        /// <summary>
        /// Корневая страница контроллера
        /// </summary>
        /// <returns></returns>
        public ActionResult Index()
        {
            return RedirectToAction("Top");
        }

        /// <summary>
        /// Отображает топ объявления из базы
        /// </summary>
        /// <returns></returns>
        public ActionResult Top()
        {
            // Навигация
            AddNavigationItem("Объявления",Url.Action("Index"));
            AddNavigationItem("Топ",Url.Action("Top"),true);

            return View();
        }

        /// <summary>
        /// Рендерит партиал вью с топом объявлений по указанным категориям
        /// </summary>
        /// <param name="category">Тип категорий для фильтра</param>
        /// <returns>Партиал вью с элементами</returns>
        public ActionResult TopAds(string category = "all")
        {
            // Репозиториий
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var imagesRepository = Locator.GetService<IImagesRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();
            Category cat = null;

            // Выбираем
            var dbAds = Enumerable.Empty<Adv>();
            switch (category)
            {
                case "all":
                    dbAds = adsRepository.FindAll();
                    break;
                case "estate":
                    cat = categoriesRepository.Load(CategoryManager.AdsRealEstateCategory);
                    dbAds = adsRepository.GetAdsInCategoryAndChildCategories(cat);
                    break;
                case "auto":
                    cat = categoriesRepository.Load(CategoryManager.AdsAutoCategory);
                    dbAds = adsRepository.GetAdsInCategoryAndChildCategories(cat);
                    break;
                case "business":
                    cat = categoriesRepository.Load(CategoryManager.AdsBusinessCategory);
                    dbAds = adsRepository.GetAdsInCategoryAndChildCategories(cat);
                    break;
                case "trade":
                    cat = categoriesRepository.Load(CategoryManager.AdsTradeCategory);
                    dbAds = adsRepository.GetAdsInCategoryAndChildCategories(cat);
                    break;
                case "construction":
                    cat = categoriesRepository.Load(CategoryManager.AdsConstructionCategory);
                    dbAds = adsRepository.GetAdsInCategoryAndChildCategories(cat);
                    break;
                case "services":
                    cat = categoriesRepository.Load(CategoryManager.AdsServicesCategory);
                    dbAds = adsRepository.GetAdsInCategoryAndChildCategories(cat);
                    break;
                case "misc":
                    cat = categoriesRepository.Load(CategoryManager.AdsMiscCategory);
                    dbAds = adsRepository.GetAdsInCategoryAndChildCategories(cat);
                    break;
            }

            // Выбираем только топ 4
            dbAds = dbAds.Where(a => a.Status == AdvStatuses.Active && a.AdvImages.Count > 0).OrderByDescending(c => c.Sticked).ThenByDescending(c => c.Views).Take(4);

            // Преобразуем в модели
            var ads = dbAds.Select(c => new AdsListItemModel(c,imagesRepository)
                                            {
                                                ThumbImageURI = c.AdvImages.Count > 0 ? GetThumbImageUrl(c.AdvImages.First().ImageURI,260,130,imagesRepository) : AdsListItemModel.NoImageURL
                                            });

            return PartialView(ads);
        }

        /// <summary>
        /// Отображает объявления автотранспорта или страницу выбора категорий автотранспорта
        /// </summary>
        /// <param name="id">Идентификатор категории, если null то обрабатываются категории автотранспорта</param>
        /// <returns></returns>
        public ActionResult Auto(long? id)
        {
            // Добавляем корневой элемент цепочки навигаций
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Авто / мото", Url.Action("Auto", new { id = new Nullable<int>() }));

            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // Имя вью который отображаем
            string viewName = "AdsCategories";
            ViewBag.activeTabIndex = 1;
            ViewBag.categoryUrlFormat = Url.Action("Auto",new {id = new Nullable<int>()}) + "/{0}";
            Func<Category, int> itemsCountSelector = adsRepository.GetAdsCountInCategoryAndChildCategories;
            ViewBag.itemsCountSelector = itemsCountSelector;
            Category category = null;
            ViewBag.titleImg = Url.Content("~/Content/images/top/estate.png");

            // В зависимости от параметров запроса
            if (!id.HasValue)
            {
                // Отображаем категории
                category = categoriesRepository.Load(CategoryManager.AdsAutoCategory);
                viewName = "AdsList";
            }
            else
            {
                // Загружаем категорию
                category = categoriesRepository.Load(id.Value);
                if (category == null)
                {
                    return View("CategoryNotFound", id);
                }

                // Формирует навигационную цепочку
                foreach (var parent in category.GetParentCategories().Skip(2))
                {
                    AddNavigationItem(parent.DisplayName,Url.Action("Auto",new {id = parent.Id}));
                }
                AddNavigationItem(category.DisplayName,String.Empty,true);

                // Присваиваем тип вью
                viewName = "AdsList";
            }

            // Отдаем основные данные во вью
            ViewBag.category = category;
            return View(viewName);
        }

        /// <summary>
        /// Отображает бизнес объявления или корневые категории объявлений для бизнеса
        /// </summary>
        /// <returns>Идентификатор категории, если null то отображаются все категории бизнеса</returns>
        public ActionResult Business(long? id)
        {
            // Добавляем корневой элемент цепочки навигаций
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Бизнес", Url.Action("Business", new { id = new Nullable<int>() }));

            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // Имя вью который отображаем
            string viewName = "AdsCategories";
            ViewBag.activeTabIndex = 2;
            ViewBag.categoryUrlFormat = Url.Action("Business", new { id = new Nullable<int>() }) + "/{0}";
            Func<Category, int> itemsCountSelector = adsRepository.GetAdsCountInCategoryAndChildCategories;
            ViewBag.itemsCountSelector = itemsCountSelector;
            Category category = null;
            ViewBag.titleImg = Url.Content("~/Content/images/top/estate.png");

            // В зависимости от параметров запроса
            if (!id.HasValue)
            {
                // Отображаем категории
                category = categoriesRepository.Load(CategoryManager.AdsBusinessCategory);
                viewName = "AdsList";
            }
            else
            {
                // Загружаем категорию
                category = categoriesRepository.Load(id.Value);
                if (category == null)
                {
                    return View("CategoryNotFound", id);
                }

                // Формирует навигационную цепочку
                foreach (var parent in category.GetParentCategories().Skip(2))
                {
                    AddNavigationItem(parent.DisplayName, Url.Action("Business", new { id = parent.Id }));
                }
                AddNavigationItem(category.DisplayName, String.Empty, true);

                // Присваиваем тип вью
                viewName = "AdsList";
            }

            // Отдаем основные данные во вью
            ViewBag.category = category;
            return View(viewName);
        }

        /// <summary>
        /// Отображает объявления или категории объявлений купли продажи
        /// </summary>
        /// <param name="id">Идентификатор категории объявлений, если нет то отображаются все корневые категории</param>
        /// <returns></returns>
        public ActionResult Trade(long? id)
        {
            // Добавляем корневой элемент цепочки навигаций
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Купля / продажа", Url.Action("Trade", new { id = new Nullable<int>() }));

            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // Имя вью который отображаем
            string viewName = "AdsCategories";
            ViewBag.activeTabIndex = 3;
            ViewBag.categoryUrlFormat = Url.Action("Trade", new { id = new Nullable<int>() }) + "/{0}";
            Func<Category, int> itemsCountSelector = adsRepository.GetAdsCountInCategoryAndChildCategories;
            ViewBag.itemsCountSelector = itemsCountSelector;
            Category category = null;
            ViewBag.titleImg = Url.Content("~/Content/images/top/estate.png");

            // В зависимости от параметров запроса
            if (!id.HasValue)
            {
                // Отображаем категории
                category = categoriesRepository.Load(CategoryManager.AdsTradeCategory);
                viewName = "AdsList";
            }
            else
            {
                // Загружаем категорию
                category = categoriesRepository.Load(id.Value);
                if (category == null)
                {
                    return View("CategoryNotFound", id);
                }

                // Формирует навигационную цепочку
                foreach (var parent in category.GetParentCategories().Skip(2))
                {
                    AddNavigationItem(parent.DisplayName, Url.Action("Trade", new { id = parent.Id }));
                }
                AddNavigationItem(category.DisplayName, String.Empty, true);

                // Присваиваем тип вью
                viewName = "AdsList";
            }

            // Отдаем основные данные во вью
            ViewBag.category = category;
            return View(viewName);
        }

        /// <summary>
        /// Отображает объявления или категории объявлений недвижимости
        /// </summary>
        /// <param name="id">Идентификатор категории объявлений, если нет то отображаются все корневые категории</param>
        /// <returns></returns>
        public ActionResult RealEstate(long? id)
        {
            // Добавляем корневой элемент цепочки навигаций
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Недвижимость", Url.Action("RealEstate", new { id = new Nullable<int>() }));

            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // Имя вью который отображаем
            string viewName = "AdsCategories";
            ViewBag.activeTabIndex = 4;
            ViewBag.categoryUrlFormat = Url.Action("RealEstate", new { id = new Nullable<int>() }) + "/{0}";
            Func<Category, int> itemsCountSelector = adsRepository.GetAdsCountInCategoryAndChildCategories;
            ViewBag.itemsCountSelector = itemsCountSelector;
            Category category = null;
            ViewBag.titleImg = Url.Content("~/Content/images/top/estate.png");

            // В зависимости от параметров запроса
            if (!id.HasValue)
            {
                // Отображаем категории
                category = categoriesRepository.Load(CategoryManager.AdsRealEstateCategory);
                viewName = "AdsList";
            }
            else
            {
                // Загружаем категорию
                category = categoriesRepository.Load(id.Value);
                if (category == null)
                {
                    return View("CategoryNotFound", id);
                }

                // Формирует навигационную цепочку
                foreach (var parent in category.GetParentCategories().Skip(2))
                {
                    AddNavigationItem(parent.DisplayName, Url.Action("RealEstate", new { id = parent.Id }));
                }
                AddNavigationItem(category.DisplayName, String.Empty, true);

                // Присваиваем тип вью
                viewName = "AdsList";
            }

            // Отдаем основные данные во вью
            ViewBag.category = category;
            return View(viewName);
        }

        /// <summary>
        /// Отображает объявления или категории объявлений по строительству
        /// </summary>
        /// <param name="id">Идентификатор категории объявлений, если нет то отображаются все корневые категории</param>
        /// <returns></returns>
        public ActionResult Construction(long? id)
        {
            // Добавляем корневой элемент цепочки навигаций
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Строительство", Url.Action("Construction", new { id = new Nullable<int>() }));

            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // Имя вью который отображаем
            string viewName = "AdsCategories";
            ViewBag.activeTabIndex = 5;
            ViewBag.categoryUrlFormat = Url.Action("Construction", new { id = new Nullable<int>() }) + "/{0}";
            Func<Category, int> itemsCountSelector = adsRepository.GetAdsCountInCategoryAndChildCategories;
            ViewBag.itemsCountSelector = itemsCountSelector;
            Category category = null;
            ViewBag.titleImg = Url.Content("~/Content/images/top/estate.png");

            // В зависимости от параметров запроса
            if (!id.HasValue)
            {
                // Отображаем категории
                category = categoriesRepository.Load(CategoryManager.AdsConstructionCategory);
                viewName = "AdsList";
            }
            else
            {
                // Загружаем категорию
                category = categoriesRepository.Load(id.Value);
                if (category == null)
                {
                    return View("CategoryNotFound", id);
                }

                // Формирует навигационную цепочку
                foreach (var parent in category.GetParentCategories().Skip(2))
                {
                    AddNavigationItem(parent.DisplayName, Url.Action("Construction", new { id = parent.Id }));
                }
                AddNavigationItem(category.DisplayName, String.Empty, true);

                // Присваиваем тип вью
                viewName = "AdsList";
            }

            // Отдаем основные данные во вью
            ViewBag.category = category;
            return View(viewName);
        }

        /// <summary>
        /// Отображает объявления или категории объявлений по услуг
        /// </summary>
        /// <param name="id">Идентификатор категории объявлений, если нет то отображаются все корневые категории</param>
        /// <returns></returns>
        public ActionResult Services(long? id)
        {
            // Добавляем корневой элемент цепочки навигаций
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Услуги", Url.Action("Services", new { id = new Nullable<int>() }));

            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // Имя вью который отображаем
            string viewName = "AdsCategories";
            ViewBag.activeTabIndex = 6;
            ViewBag.categoryUrlFormat = Url.Action("Services", new { id = new Nullable<int>() }) + "/{0}";
            Func<Category, int> itemsCountSelector = adsRepository.GetAdsCountInCategoryAndChildCategories;
            ViewBag.itemsCountSelector = itemsCountSelector;
            Category category = null;
            ViewBag.titleImg = Url.Content("~/Content/images/top/estate.png");

            // В зависимости от параметров запроса
            if (!id.HasValue)
            {
                // Отображаем категории
                category = categoriesRepository.Load(CategoryManager.AdsServicesCategory);
                viewName = "AdsList";
            }
            else
            {
                // Загружаем категорию
                category = categoriesRepository.Load(id.Value);
                if (category == null)
                {
                    return View("CategoryNotFound", id);
                }

                // Формирует навигационную цепочку
                foreach (var parent in category.GetParentCategories().Skip(2))
                {
                    AddNavigationItem(parent.DisplayName, Url.Action("Services", new { id = parent.Id }));
                }
                AddNavigationItem(category.DisplayName, String.Empty, true);

                // Присваиваем тип вью
                viewName = "AdsList";
            }

            // Отдаем основные данные во вью
            ViewBag.category = category;
            return View(viewName);
        }

        /// <summary>
        /// Отображает объявления или категории объявлений по другим тематикам
        /// </summary>
        /// <param name="id">Идентификатор категории объявлений, если нет то отображаются все корневые категории</param>
        /// <returns></returns>
        public ActionResult Misc(long? id)
        {
            // Добавляем корневой элемент цепочки навигаций
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Другое", Url.Action("Misc", new { id = new Nullable<int>() }));

            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // Имя вью который отображаем
            string viewName = "AdsCategories";
            ViewBag.activeTabIndex = 7;
            ViewBag.categoryUrlFormat = Url.Action("Misc", new { id = new Nullable<int>() }) + "/{0}";
            Func<Category, int> itemsCountSelector = adsRepository.GetAdsCountInCategoryAndChildCategories;
            ViewBag.itemsCountSelector = itemsCountSelector;
            Category category = null;
            ViewBag.titleImg = Url.Content("~/Content/images/top/estate.png");

            // В зависимости от параметров запроса
            if (!id.HasValue)
            {
                // Отображаем категории
                category = categoriesRepository.Load(CategoryManager.AdsMiscCategory);
                viewName = "AdsList";
            }
            else
            {
                // Загружаем категорию
                category = categoriesRepository.Load(id.Value);
                if (category == null)
                {
                    return View("CategoryNotFound", id);
                }

                // Формирует навигационную цепочку
                foreach (var parent in category.GetParentCategories().Skip(2))
                {
                    AddNavigationItem(parent.DisplayName, Url.Action("Misc", new { id = parent.Id }));
                }
                AddNavigationItem(category.DisplayName, String.Empty, true);

                // Присваиваем тип вью
                viewName = "AdsList";
            }

            // Отдаем основные данные во вью
            ViewBag.category = category;
            return View(viewName);
        }

        #endregion

        #region просмотр списка объявлений

        /// <summary>
        /// Отдает контрол с объявлениями из указанной категории а так же всех дочерних подкатегорий, поддерживается пейджионирование
        /// </summary>
        /// <param name="categoryId">Идентификатор категории</param>
        /// <param name="page">Страница которую отобразить</param>
        /// <param name="itemsPerPage">Количество элементов на странице</param>
        /// <returns></returns>
        public ActionResult AdvsView(long categoryId, int page = 0, int itemsPerPage = 24)
        {
            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // загружаем указанную категорию
            var category = categoriesRepository.Load(categoryId);
            if (category == null)
            {
                return Content(String.Format("Категория с идентификатором {0} не найдена", categoryId));
            }

            // Узнаем количество объявлений в указанной категории
            var adsCount = adsRepository.GetAdsCountInCategoryAndChildCategories(category);

            // Отдаем данные во вью
            ViewBag.category = category;
            ViewBag.page = page;
            ViewBag.itemsPerPage = itemsPerPage;
            ViewBag.adsCount = adsCount;

            // Отдаем сам вью
            return PartialView();
        }

        /// <summary>
        /// Возвращает только объявления из указанной категории с указанным разбиением на страницы
        /// </summary>
        /// <param name="categoryId">Идентификатор категории</param>
        /// <param name="page">Страница</param>
        /// <param name="itemsPerPage">Количество элементов на странице</param>
        /// <returns>Разметка с объявлениями</returns>
        public ActionResult GetAdvs(long categoryId, int page, int itemsPerPage)
        {
            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();
            var imagesRep = Locator.GetService<IImagesRepository>();

            // загружаем указанную категорию
            var category = categoriesRepository.Load(categoryId);
            if (category == null)
            {
                return Content(String.Format("Категория с идентификатором {0} не найдена", categoryId));
            }

            // получаем указанные объявления
            var advs = adsRepository.GetAdsInCategoryAndChildCategories(category).Where(a => a.Status == AdvStatuses.Active).OrderByDescending(a => a.Sticked).ThenByDescending(a => a.DateCreated).Skip(page*itemsPerPage).Take(itemsPerPage);

            // Преобразуем в модели
            var ads = advs.Select(a => new AdsListItemModel(a, imagesRep)
                                           {
                                               ThumbImageURI = 
                                                   a.AdvImages.Count > 0
                                                       ? GetThumbImageUrl(a.AdvImages.First().ImageURI, 260, 130,
                                                                          imagesRep)
                                                       : AdsListItemModel.NoImageURL
                                           });

            // Отдаем сам вью
            return PartialView(ads);
        }

        #endregion

        #region Персональное

        /// <summary>
        /// Отображает страницу моих объявлений
        /// </summary>
        /// <param name="id">Идентификатор подкатегории которую отобразить</param>
        /// <returns></returns>
        public ActionResult My(long? id)
        {
            // Проверка что пользователь авторизован
            if (CurrentUser == null)
            {
                return RedirectToAction("RegistrationRequired", "Home");
            }

            // Навигационная цепочка
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Мои объявления", Url.Action("My"),!id.HasValue);

            // Репозиторий
            var categoriesRep = Locator.GetService<ICategoriesRepository>();
            var adsRepository = Locator.GetService<IAdvsRepository>();

            // Загружаем категорию
            Category category = null;
            if (id.HasValue)
            {
                category = categoriesRep.Load(id.Value);
                if (category == null)
                {
                    return View("CategoryNotFound", id);
                }

                // Формирует навигационную цепочку
                foreach (var parent in category.GetParentCategories().Skip(1))
                {
                    AddNavigationItem(parent.DisplayName, Url.Action("My", new { id = parent.Id }));
                }
                AddNavigationItem(category.DisplayName, String.Empty, true);
            }
            else
            {
                category = categoriesRep.Load(CategoryManager.RootAdsCategory);
            }

            // Отдаем данные во вью
            ViewBag.category = category;
            ViewBag.categoryUrlFormat = Url.Action("My", new { id = new Nullable<int>() }) + "/{0}";
            Func<Category, int> itemsCountSelector = (cat) => adsRepository.GetUserAdsCountInCategoryAndChildCategories(cat,CurrentUser);
            ViewBag.itemsCountSelector = itemsCountSelector;
            ViewBag.titleImg = Url.Content("~/Content/images/top/estate.png");

            // Отдаем вью
            return View("MyAds");
        }

        /// <summary>
        /// Отдает контрол с объявлениями созданными текущим пользователем из указанной категории а так же всех дочерних подкатегорий, поддерживается пейджионирование
        /// </summary>
        /// <param name="categoryId">Идентификатор категории</param>
        /// <param name="page">Страница которую отобразить</param>
        /// <param name="itemsPerPage">Количество элементов на странице</param>
        /// <returns></returns>
        public ActionResult MyAdvsView(long categoryId, int page = 0, int itemsPerPage = 24)
        {
            // Проверяем что пользователь есть
            if (CurrentUser == null)
            {
                return Content("Пользователь не авторизован");
            }
            
            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // загружаем указанную категорию
            var category = categoriesRepository.Load(categoryId);
            if (category == null)
            {
                return Content(String.Format("Категория с идентификатором {0} не найдена", categoryId));
            }

            // Узнаем количество объявлений в указанной категории
            var adsCount = adsRepository.GetUserAdsCountInCategoryAndChildCategories(category,CurrentUser);

            // Отдаем данные во вью
            ViewBag.category = category;
            ViewBag.page = page;
            ViewBag.itemsPerPage = itemsPerPage;
            ViewBag.adsCount = adsCount;

            // Отдаем сам вью
            return PartialView();
        }

        /// <summary>
        /// Возвращает только объявления созданные теккущим пользователем из указанной категории с указанным разбиением на страницы
        /// </summary>
        /// <param name="categoryId">Идентификатор категории</param>
        /// <param name="page">Страница</param>
        /// <param name="itemsPerPage">Количество элементов на странице</param>
        /// <returns>Разметка с объявлениями</returns>
        public ActionResult GetMyAdvs(long categoryId, int page, int itemsPerPage)
        {
            // Проверяем что пользователь есть
            if (CurrentUser == null)
            {
                return Content("Пользователь не авторизован");
            }
            
            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();
            var imagesRep = Locator.GetService<IImagesRepository>();

            // загружаем указанную категорию
            var category = categoriesRepository.Load(categoryId);
            if (category == null)
            {
                return Content(String.Format("Категория с идентификатором {0} не найдена", categoryId));
            }

            // получаем указанные объявления
            var advs = adsRepository.GetUserAdsInCategoryAndChildCategories(category,CurrentUser).Where(a => a.Status == AdvStatuses.Active).OrderByDescending(a => a.Sticked).ThenByDescending(a => a.DateCreated).Skip(page * itemsPerPage).Take(itemsPerPage);

            // Преобразуем в модели
            var ads = advs.Select(a => new AdsListItemModel(a, imagesRep)
            {
                ThumbImageURI =
                    a.AdvImages.Count > 0
                        ? GetThumbImageUrl(a.AdvImages.First().ImageURI, 260, 130,
                                           imagesRep)
                        : AdsListItemModel.NoImageURL
            });

            // Отдаем сам вью
            return PartialView("GetAdvs",ads);
        }

        #endregion

        #region Избранное

        /// <summary>
        /// Отображает избранные объявления пользователя
        /// </summary>
        /// <param name="id">Идентификатор категории</param>
        /// <returns>Страница избранныъ объявлений</returns>
        public ActionResult Favourites(long? id)
        {
            // Проверка что пользователь авторизован
            if (CurrentUser == null)
            {
                return RedirectToAction("RegistrationRequired", "Home");
            }

            // Навигационная цепочка
            AddNavigationItem("Объявления", Url.Action("Index"));
            AddNavigationItem("Избранные объявления", Url.Action("My"), !id.HasValue);

            // Репозиторий
            var categoriesRep = Locator.GetService<ICategoriesRepository>();
            var adsRepository = Locator.GetService<IAdvsRepository>();

            // Загружаем категорию
            Category category = null;
            if (id.HasValue)
            {
                category = categoriesRep.Load(id.Value);
                if (category == null)
                {
                    return View("CategoryNotFound", id);
                }

                // Формирует навигационную цепочку
                foreach (var parent in category.GetParentCategories().Skip(1))
                {
                    AddNavigationItem(parent.DisplayName, Url.Action("Favourites", new { id = parent.Id }));
                }
                AddNavigationItem(category.DisplayName, String.Empty, true);
            }
            else
            {
                category = categoriesRep.Load(CategoryManager.RootAdsCategory);
            }

            // Отдаем данные во вью
            ViewBag.category = category;
            ViewBag.categoryUrlFormat = Url.Action("Favourites", new { id = new Nullable<int>() }) + "/{0}";
            Func<Category, int> itemsCountSelector = (cat) => adsRepository.GetFavouriteUserAdsCountInCategoryAndChildCategories(cat, CurrentUser);
            ViewBag.itemsCountSelector = itemsCountSelector;
            ViewBag.titleImg = Url.Content("~/Content/images/top/estate.png");

            // Отдаем вью
            return View();
        }

        /// <summary>
        /// Отдает контрол с объявлениями избранными текущим пользователем из указанной категории а так же всех дочерних подкатегорий, поддерживается пейджионирование
        /// </summary>
        /// <param name="categoryId">Идентификатор категории</param>
        /// <param name="page">Страница которую отобразить</param>
        /// <param name="itemsPerPage">Количество элементов на странице</param>
        /// <returns></returns>
        public ActionResult FavouriteAdvsView(long categoryId, int page = 0, int itemsPerPage = 24)
        {
            // Проверяем что пользователь есть
            if (CurrentUser == null)
            {
                return Content("Пользователь не авторизован");
            }

            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();

            // загружаем указанную категорию
            var category = categoriesRepository.Load(categoryId);
            if (category == null)
            {
                return Content(String.Format("Категория с идентификатором {0} не найдена", categoryId));
            }

            // Узнаем количество объявлений в указанной категории
            var adsCount = adsRepository.GetUserAdsCountInCategoryAndChildCategories(category, CurrentUser);

            // Отдаем данные во вью
            ViewBag.category = category;
            ViewBag.page = page;
            ViewBag.itemsPerPage = itemsPerPage;
            ViewBag.adsCount = adsCount;

            // Отдаем сам вью
            return PartialView();
        }

        /// <summary>
        /// Возвращает только объявления добавленное в избранное теккущим пользователем из указанной категории с указанным разбиением на страницы
        /// </summary>
        /// <param name="categoryId">Идентификатор категории</param>
        /// <param name="page">Страница</param>
        /// <param name="itemsPerPage">Количество элементов на странице</param>
        /// <returns>Разметка с объявлениями</returns>
        public ActionResult GetFavouriteAdvs(long categoryId, int page, int itemsPerPage)
        {
            // Проверяем что пользователь есть
            if (CurrentUser == null)
            {
                return Content("Пользователь не авторизован");
            }

            // Репозитории
            var adsRepository = Locator.GetService<IAdvsRepository>();
            var categoriesRepository = Locator.GetService<ICategoriesRepository>();
            var imagesRep = Locator.GetService<IImagesRepository>();

            // загружаем указанную категорию
            var category = categoriesRepository.Load(categoryId);
            if (category == null)
            {
                return Content(String.Format("Категория с идентификатором {0} не найдена", categoryId));
            }

            // получаем указанные объявления
            var advs = adsRepository.GetFavouriteUserAdsInCategoryAndChildCategories(category, CurrentUser).Where(a => a.Status == AdvStatuses.Active).OrderByDescending(a => a.Sticked).ThenByDescending(a => a.DateCreated).Skip(page * itemsPerPage).Take(itemsPerPage);

            // Преобразуем в модели
            var ads = advs.Select(a => new AdsListItemModel(a, imagesRep)
            {
                ThumbImageURI =
                    a.AdvImages.Count > 0
                        ? GetThumbImageUrl(a.AdvImages.First().ImageURI, 260, 130,
                                           imagesRep)
                        : AdsListItemModel.NoImageURL
            });

            // Отдаем сам вью
            return PartialView("GetAdvs", ads);
        }
        
        /// <summary>
        /// Добавляет объявление с указанным идентификатором в избранное
        /// </summary>
        /// <param name="id">Идентификатор объявления</param>
        /// <returns>JSON статус операции</returns>
        [HttpPost]
        public JsonResult AddFavourites(long id)
        {
            try
            {
                // Проверяем что пользователь авторизован
                if (CurrentUser == null)
                {
                    throw new Exception("Текущий пользователь не авторизован");
                }

                // Репозиторий
                var repository = Locator.GetService<IAdvsRepository>();

                // Загружаем объявление
                var ads = repository.Load(id);
                if (ads == null)
                {
                    throw new ObjectNotFoundException(String.Format("Объявление с идентификатором {0} не найдено",id));
                }

                // Добавляем в избранное
                repository.AddAdsToUserFavourites(CurrentUser,ads);
                repository.SubmitChanges();

                // Отдаем результат
                return Json(new {success = true});
            }
            catch (Exception e)
            {
                Response.StatusCode = 500;
                return Json(new {success = false, message = e.Message});
            }
        }

        /// <summary>
        /// Удаляет объявление с указанным идентификатором из избранного
        /// </summary>
        /// <param name="id">Идентификатор объявления</param>
        /// <returns></returns>
        [HttpPost]
        public JsonResult RemoveFavourites(long id)
        {
            try
            {
                // Проверяем что пользователь авторизован
                if (CurrentUser == null)
                {
                    throw new Exception("Текущий пользователь не авторизован");
                }

                // Репозиторий
                var repository = Locator.GetService<IAdvsRepository>();

                // Загружаем объявление
                var ads = repository.Load(id);
                if (ads == null)
                {
                    throw new ObjectNotFoundException(String.Format("Объявление с идентификатором {0} не найдено", id));
                }

                // Удаляем из избранного
                repository.RemoveAdsFromUserFavourites(CurrentUser, ads);
                repository.SubmitChanges();

                // Отдаем результат
                return Json(new { success = true });
            }
            catch (Exception e)
            {
                Response.StatusCode = 500;
                return Json(new { success = false, message = e.Message });
            }
        }

        #endregion
    }
}
